package aceim.app.utils; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.concurrent.Executors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import aceim.api.dataentity.FileProgress; import aceim.api.utils.Logger; import aceim.api.utils.Logger.LoggerLevel; import aceim.api.utils.Utils; import aceim.app.dataentity.Account; import aceim.app.service.CoreService; import aceim.app.service.IUserInterface; import aceim.app.service.ServiceUtils; import android.content.Context; import android.os.Environment; import android.text.format.DateFormat; public final class ImportAndExport { private static String root = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "AceImTmp" + File.separator; private static final int TOTAL_PROGRESS = 10000; private ImportAndExport() { } public static final void exportData(final FileProgress progress, final String password, final IUserInterface ui, final Context context) { Logger.log("Export data request", LoggerLevel.VERBOSE); Executors.defaultThreadFactory().newThread(new Runnable() { @Override public void run() { File[] files = context.getApplicationContext().getFilesDir().listFiles(); FileProgress p = new FileProgress(progress.getServiceId(), progress.getMessageId(), "", TOTAL_PROGRESS, 10, progress.isIncoming(), progress.getOwnerUid(), null); try { ui.onFileProgress(p); } catch (Exception e1) { Logger.log(e1); } zipAndEncode(progress, files, password, ui, context); } }).start(); } private static void zipAndEncode(FileProgress progress, File[] files, String password, IUserInterface ui, Context context) { Logger.log("Going to zip: " + Arrays.toString(files), LoggerLevel.VERBOSE); File target = Utils.createLocalFileForReceiving("Export " + DateFormat.getLongDateFormat(context).format(Calendar.getInstance().getTime()), null, 0); FileOutputStream fos = null; try { fos = new FileOutputStream(target); CipherOutputStream cos = new CipherOutputStream(new BufferedOutputStream(fos), generateKey(password, Cipher.ENCRYPT_MODE)); ZipOutputStream zos = new ZipOutputStream(cos); byte[] buffer = new byte[2048]; FileProgress p = new FileProgress(progress.getServiceId(), progress.getMessageId(), target.getAbsolutePath(), TOTAL_PROGRESS, progress.getSentBytes() + 5, progress.isIncoming(), progress.getOwnerUid(), null); try { ui.onFileProgress(p); } catch (Exception e1) { Logger.log(e1); } for (File file : files) { FileInputStream in = new FileInputStream(file); zos.putNextEntry(new ZipEntry(file.getName())); Logger.log("Zipping: " + file, LoggerLevel.VERBOSE); p = new FileProgress(p.getServiceId(), p.getMessageId(), target.getAbsolutePath(), TOTAL_PROGRESS, p.getSentBytes() + 5, p.isIncoming(), p.getOwnerUid(), null); try { ui.onFileProgress(p); } catch (Exception e1) { Logger.log(e1); } int len; while ((len = in.read(buffer)) > 0) { zos.write(buffer, 0, len); } zos.closeEntry(); in.close(); } zos.close(); p = new FileProgress(progress.getServiceId(), progress.getMessageId(), progress.getFilePath(), TOTAL_PROGRESS, TOTAL_PROGRESS, progress.isIncoming(), progress.getOwnerUid(), null); try { ui.onFileProgress(p); } catch (Exception e1) { Logger.log(e1); } Logger.log("Export succeded to: " + target, LoggerLevel.VERBOSE); } catch (Exception e) { Logger.log(e); FileProgress p = new FileProgress(progress.getServiceId(), progress.getMessageId(), progress.getFilePath(), TOTAL_PROGRESS, TOTAL_PROGRESS, progress.isIncoming(), progress.getOwnerUid(), e.toString()); try { ui.onFileProgress(p); } catch (Exception e1) { Logger.log(e1); } try { if (fos != null) { fos.close(); } target.delete(); } catch (IOException e1) { Logger.log(e1); } } } public static final void importData(final FileProgress progress, final String password, final IUserInterface ui, final CoreService service) { Logger.log("Import data request from " + progress.getFilePath(), LoggerLevel.VERBOSE); Executors.defaultThreadFactory().newThread(new Runnable() { @Override public void run() { try { File tmpFolder = new File(root); tmpFolder.mkdirs(); File[] files = decodeAndUnzip(progress, password, ui, service); for (File file : files) { importFile(file, progress, ui, service); } FileProgress p = new FileProgress(progress.getServiceId(), progress.getMessageId(), progress.getFilePath(), TOTAL_PROGRESS, TOTAL_PROGRESS, progress.isIncoming(), progress.getOwnerUid(), null); try { ui.onFileProgress(p); } catch (Exception e1) { Logger.log(e1); } for (File file : tmpFolder.listFiles()) { file.delete(); } tmpFolder.delete(); try { ui.terminate(); } catch (Exception e) { Logger.log(e); } service.exitService(true); } catch (Exception e) { Logger.log(e); FileProgress p = new FileProgress(progress.getServiceId(), progress.getMessageId(), progress.getFilePath(), TOTAL_PROGRESS, 10, progress.isIncoming(), progress.getOwnerUid(), e.getMessage()); try { ui.onFileProgress(p); } catch (Exception e1) { Logger.log(e1); } } } }).start(); } private static void importFile(File file, FileProgress progress, IUserInterface ui, CoreService coreService) { Logger.log("Importing: " + file, LoggerLevel.VERBOSE); try { FileInputStream fis = new FileInputStream(file); if ("XmlTotalParams".equals(file.getName())) { List<Account> accounts = DataStorage.readFileWithAccountList(fis, "UTF-16LE"); coreService.importAccounts(accounts); } else { FileOutputStream fos = coreService.openFileOutput(file.getName(), ServiceUtils.getAccessMode()); copyFile(fis, fos); fos.close(); } fis.close(); } catch (Exception e) { Logger.log(e); } } private static void copyFile(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } } private static final File[] decodeAndUnzip(FileProgress progress, String password, IUserInterface ui, Context context) { Logger.log("Unzipping...", LoggerLevel.VERBOSE); FileInputStream fis = null; try { fis = new FileInputStream(progress.getFilePath()); CipherInputStream cis = new CipherInputStream(new BufferedInputStream(fis), generateKey(password, Cipher.DECRYPT_MODE)); ZipInputStream zis = new ZipInputStream(cis); String filename; ZipEntry ze; int count; byte[] buffer = new byte[2048]; FileProgress p = new FileProgress(progress.getServiceId(), progress.getMessageId(), progress.getFilePath(), TOTAL_PROGRESS, 10, progress.isIncoming(), progress.getOwnerUid(), progress.getError()); try { ui.onFileProgress(p); } catch (Exception e) { Logger.log(e); } while ((ze = zis.getNextEntry()) != null) { filename = ze.getName(); p = new FileProgress(progress.getServiceId(), progress.getMessageId(), progress.getFilePath(), TOTAL_PROGRESS, p.getSentBytes() + 1, progress.isIncoming(), progress.getOwnerUid(), progress.getError()); try { ui.onFileProgress(p); } catch (Exception e) { Logger.log(e); } if (ze.isDirectory()) { Logger.log("Dir found: " + filename, LoggerLevel.VERBOSE); File fmd = new File(root + filename); fmd.mkdirs(); continue; } Logger.log("File found: " + filename, LoggerLevel.VERBOSE); FileOutputStream fout = new FileOutputStream(root + filename); while ((count = zis.read(buffer)) != -1) { fout.write(buffer, 0, count); } Logger.log("File unzipped: " + filename, LoggerLevel.VERBOSE); fout.close(); zis.closeEntry(); } zis.close(); p = new FileProgress(progress.getServiceId(), progress.getMessageId(), progress.getFilePath(), TOTAL_PROGRESS, TOTAL_PROGRESS / 2, progress.isIncoming(), progress.getOwnerUid(), progress.getError()); try { ui.onFileProgress(p); } catch (Exception e) { Logger.log(e); } File[] files = new File(root).listFiles(); Logger.log("Successfully unzipped: " + Arrays.toString(files), LoggerLevel.VERBOSE); return files; } catch (Exception e) { Logger.log(e); if (fis != null) { try { fis.close(); } catch (IOException e1) { Logger.log(e1); } } return new File[0]; } } private static Cipher generateKey(String password, int cipherMode) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException { byte[] keyStart = password.getBytes("UTF-8"); KeyGenerator kgen = KeyGenerator.getInstance("AES"); SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto"); sr.setSeed(keyStart); kgen.init(128, sr); SecretKey skey = kgen.generateKey(); byte[] key = skey.getEncoded(); SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(cipherMode, skeySpec); return cipher; } }